#ifndef xpack_io_private_file
#define xpack_io_private_file
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::io_file::inc
#include"concurrency/lock/atom_swap.hpp"
#include"configure/switch.hpp"
#include"define/base_type.hpp"
#include"io/file.hpp"
#include"io/dir.hpp"
#include"io/private/path_buffer.hpp"
#include"lang/cxx/index_of_last.hpp"
#include"lang/cxx.hpp"
#include"macro/xindex_rollback.hpp"
#include"macro/xdefer.hpp"
#include"macro/xexport.hpp"
#include"macro/xmaybe_unused.hpp"
#include"macro/xwarning.hpp"
#pragma pop_macro("xuser")

namespace mixc::io_file::origin{
    file::file(file && value){
        m_path      = value.m_path;
        m_fd        = value.m_fd;
        value.m_fd  = -1;
    }
}

#if xis_windows
#include<Windows.h>
#else
#include<stdio.h>
#include<sys/file.h>
#include<sys/stat.h>
#include<unistd.h>
#include"cmd.hpp"
#endif

#define xlinux_cmd_mv       "/bin/mv", "mv"
#define xlinux_cmd_cp       "/bin/cp", "cp"

xwarning_disable(26444)

namespace mixc::io_file::inc{
    // 要求没打开文件时，内部打开并判断
    extern u64 length(xmaybe_unused ixx fd, xmaybe_unused inc::c08 const & path){
        using buff = inc::path_buffer;
        buff buf;
        auto source  = buf.alloc(path);

        xdefer{
            buf.free(source, path);
        };

    #if xis_windows
        u64 size = 0;
        if (fd < 0){
            fd        = (ixx)::CreateFileA(
                asciis(source), 
                type, 
                FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 
                0, 
                NULL
            );

            if (fd < 0){
                return 0;
            }
            ::GetFileSizeEx(HANDLE(fd), PLARGE_INTEGER(& size));
            ::CloseHandle(HANDLE(fd));
        }
        else{
            ::GetFileSizeEx(HANDLE(fd), PLARGE_INTEGER(& size));
        }
        return size;
    #elif xis_linux

        struct stat sta = {};
        ::stat(asciis(source), & sta);
        return sta.st_size;
    #else
        #error "os miss match"
    #endif
    }

    extern bool is_exist(inc::c08 const & path){
        using buff = inc::path_buffer;
        buff buf;
        auto source  = buf.alloc(path);

        xdefer{
            buf.free(source, path);
        };

    #if xis_windows
        auto word = GetFileAttributesA(asciis(path));
        auto exist = word != INVALID_FILE_ATTRIBUTES and 0 == (word & FILE_ATTRIBUTE_DIRECTORY);
    #elif xis_linux
        typedef struct stat meta_t;
        auto && meta = meta_t{};
        auto exist = stat(asciis(source), & meta) != -1 and (meta.st_mode & S_IFDIR) == 0;
    #else
        #error "os miss match"
    #endif

        return exist;
    }
}

namespace mixc::io_file::origin{
    file::file() : m_fd(-1), m_path("") {}

    bstate_t file::open(inc::c08 path, access_mode_t mode) const {
        auto type   = 0;
        auto i      = uxx(inc::c08{path}.index_of_last({'/','\\'}));

        if (i != not_exist and mode != access_mode_t::read_only){
            auto folder     = inc::c08{path};
            folder->length  = i;

            if (inc::dir{folder}->is_exist == false){
                inc::dir{folder}.create();
            }
        }

        xthe.close();

        using buff  = inc::path_buffer;
        buff buf; 
        auto source = buf.alloc(m_path = path);

        xdefer{
            buf.free(source, m_path);
        };

    #if xis_windows
        switch(mode){
        case access_mode_t::read_only:  type = GENERIC_READ;                    break;
        case access_mode_t::write_only: type = GENERIC_WRITE;                   break;
        case access_mode_t::read_write: type = GENERIC_READ | GENERIC_WRITE;    break;
        }

        m_fd        = (ixx)::CreateFileA(
            asciis(source), 
            type, 
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_NEW, 
            0, 
            NULL
        );

        if (m_fd == -1 and mode != access_mode_t::read_only){
            m_fd    = (ixx)::CreateFileA(
                asciis(source), 
                type, 
                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
                NULL, 
                OPEN_EXISTING, 
                0, 
                NULL
            );
        }

    #elif xis_linux
        switch(mode){
        case access_mode_t::read_only:  type = O_RDONLY; break;
        case access_mode_t::write_only: type = O_WRONLY; break;
        case access_mode_t::read_write: type = O_RDWR;   break;
        }

        auto origin_path = (asciis)source;

        // 其他模式
        if (type != O_RDONLY){
            if (::access(origin_path, F_OK) != 0){
                ::creat(origin_path, 0644);
            }
        }

        m_fd      = ::open(origin_path, type);

    #else
        #error "os miss match"
    #endif

        return xthe->is_open ? bstate_t::fail : bstate_t::success;
    }

    bstate_t file::close() const {
        if (not xthe->is_open){
            return bstate_t::fail;
        }

        auto old_fd = m_fd;
        m_fd        = 0; // 设置成 0， 与 is_closed 属性匹配

    #if xis_windows
        return ::CloseHandle(HANDLE(old_fd)) == FALSE ? bstate_t::fail : bstate_t::success;
    #elif xis_linux
        return ::close(old_fd) < 0 ? bstate_t::fail : bstate_t::success;
    #else
        #error "os miss match"
    #endif
    }

    bstate_t file::seek(i64 offset) const {
        if (not xthe->is_open){
            this->open(m_path, access_mode_t::read_write);
        }

    #if xis_windows
        BOOL result;

        if (offset < 0){
            offset = -offset - 1;
            result = ::SetFilePointerEx(HANDLE(m_fd), *PLARGE_INTEGER(& offset), NULL, FILE_END);
        }
        else{
            result = ::SetFilePointerEx(HANDLE(m_fd), *PLARGE_INTEGER(& offset), NULL, FILE_BEGIN);
        }

        return result == FALSE ? bstate_t::fail : bstate_t::success;

    #elif xis_linux
        i32 result;

        if (offset < 0){
            result = ::lseek(int(m_fd), (off_t)(offset + 1), SEEK_END);
        }
        else{
            result = ::lseek(int(m_fd), (off_t)(offset), SEEK_SET);
        }

        return result < 0 ? bstate_t::fail : bstate_t::success;
    #else
        #error "os miss match"
    #endif
    }

    bstate_t file::flush() const {
        if (not xthe->is_open){
            return bstate_t::fail;
        }

    #if xis_windows
        return ::FlushFileBuffers(HANDLE(m_fd)) == FALSE ? bstate_t::fail : bstate_t::success;
    #elif xis_linux
        return ::fsync(int(m_fd)) < 0 ? bstate_t::fail : bstate_t::success;;
    #else
        #error "os miss match"
    #endif
    }

    uxx file::read(voidp buffer, uxx bytes) const {
        if (not xthe->is_open){
            xthe.open(m_path, access_mode_t::read_only);
        }

    #if xis_windows
        u64 size = 0;

        if (::ReadFile(HANDLE(m_fd), buffer, DWORD(bytes), LPDWORD(& size), NULL) == FALSE){
            return not_exist;
        }
    #elif xis_linux
        uxx size = (uxx)::read(m_fd, buffer, bytes);
    #else
        #error "os miss match"
    #endif

        return uxx(size);
    }

    uxx file::write(void const * buffer, uxx bytes) const {
        if (not xthe->is_open){
            xthe.open(m_path, access_mode_t::write_only);
        }

    #if xis_windows
        u64 size = 0;
        
        if (::WriteFile(HANDLE(m_fd), buffer, DWORD(bytes), LPDWORD(& size), NULL) == FALSE){
            return not_exist;
        }
    #elif xis_linux
        uxx size = (uxx)::write(int(m_fd), buffer, bytes);
    #else
        #error "os miss match"
    #endif

        return uxx(size);
    }

    bstate_t file::remove() const{
        using buff = inc::path_buffer;
        buff buf;
        auto source = buf.alloc(m_path);
        auto is_success = 

    #if xis_windows
        ::DeleteFileA(asciis(source)) ? bstate_t::success : bstate_t::fail;
    #elif xis_linux
        ::remove(asciis(source)) == 0 ? bstate_t::success : bstate_t::fail;
    #else
        #error "os miss match"
    #endif

        buf.free(source, m_path);
        return is_success;
    }

    bstate_t file::move_to(inc::c08 new_path) const{
        using buff = inc::path_buffer;
        buff buf;
        auto source = buf.alloc(m_path);
        auto target = buf.alloc(new_path);
        auto is_success = 

    #if xis_windows
        ::MoveFileA(asciis(source), asciis(target)) ? bstate_t::success : bstate_t::fail;
    #elif xis_linux
        ::rename(asciis(source), asciis(target)) == 0 ? bstate_t::success : bstate_t::fail;
    #else
        #error "os miss match"
    #endif

        buf.free(target, new_path);
        buf.free(source, m_path);
        return is_success;
    }

    bstate_t file::move_to_forcedly(inc::c08 new_path) const {
        using buff = inc::path_buffer;
        buff buf;
        auto source = buf.alloc(m_path);
        auto target = buf.alloc(new_path);
        auto is_success = 

    #if xis_windows
        ::MoveFileExA(asciis(source), asciis(target), 
            MOVEFILE_COPY_ALLOWED |     // 允许跨分区移动，避免移动失败后丢失
            MOVEFILE_REPLACE_EXISTING   // 允许覆盖
        ) ? bstate_t::success : bstate_t::fail;

    #elif xis_linux
        cmd(xlinux_cmd_mv, source, target);
    #else
        #error "os miss match"
    #endif

        buf.free(target, new_path);
        buf.free(source, m_path);
        return is_success;
    }

    bstate_t file::copy_to(inc::c08 new_path) const{
        using buff = inc::path_buffer;
        buff buf;
        auto source = buf.alloc(m_path);
        auto target = buf.alloc(new_path);
        auto is_success = 

    #if xis_windows
        ::CopyFileA(asciis(source), asciis(target), true/*override*/) ?
            bstate_t::success : bstate_t::fail;
    #elif xis_linux
        cmd(xlinux_cmd_cp, source, target);
    #else
        #error "os miss match"
    #endif

        buf.free(target, new_path);
        buf.free(source, m_path);
        return is_success;
    }
}

#undef  xlinux_cmd_cp
#undef  xlinux_cmd_mv
#endif
